package com.quickboot.core.aspect;

import cn.hutool.extra.servlet.ServletUtil;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.quickboot.common.annotation.Log;
import com.quickboot.common.utils.SaTokenUtils;
import com.quickboot.common.utils.SpringUtils;
import com.quickboot.system.entity.SysOperationLog;
import com.quickboot.system.service.SysOperationLogService;
import io.swagger.annotations.Api;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StopWatch;

import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 操作日志切面
 *
 * @author Tellsea
 * @date 2022/12/15
 */
@Slf4j
@Aspect
@Component
@SuppressWarnings("all")
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class LogAspect {

    private final SysOperationLogService sysOperationLogService;

    @Pointcut("@annotation(com.quickboot.common.annotation.Log)")
    public void logAspect() {
    }

    @Around("logAspect()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws JsonProcessingException {
        Object result = null;
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        Method method = signature.getMethod();
        StopWatch stopWatch = new StopWatch();
        try {
            stopWatch.start();
            result = proceedingJoinPoint.proceed();
            stopWatch.stop();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        Api api = AnnotationUtils.findAnnotation(method, Api.class);
        Log log = method.getAnnotation(Log.class);
        String title = ObjectUtils.isEmpty(api) ? log.value() : api.tags() + "：" + log.value();
        SysOperationLog operationLog = new SysOperationLog();
        operationLog.setUserId(SaTokenUtils.getUserId());
        operationLog.setUserName(SaTokenUtils.getUserName());
        operationLog.setOperationIp(ServletUtil.getClientIP(SpringUtils.getRequest()));
        operationLog.setTitle(title);
        operationLog.setUsageTime(stopWatch.getTotalTimeMillis());
        operationLog.setRequestUrl(SpringUtils.getRequest().getRequestURI());
        operationLog.setRequestMethod(method.getDeclaringClass().getName() + "." + method.getName() + "()");
        operationLog.setRequestParams(JSON.toJSONString(getFieldsName(proceedingJoinPoint)));
        operationLog.setResponseResult(JSON.toJSONString(result));
        operationLog.setOperationTime(new Date());
        sysOperationLogService.save(operationLog);
        return result;
    }

    /**
     * 获取参数列表
     *
     * @param proceedingJoinPoint
     * @return
     */
    public static Map<String, Object> getFieldsName(ProceedingJoinPoint proceedingJoinPoint) {
        // 参数值
        Object[] args = proceedingJoinPoint.getArgs();
        ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        Method method = signature.getMethod();
        String[] parameterNames = pnd.getParameterNames(method);
        Map<String, Object> paramMap = new HashMap<>(32);
        for (int i = 0; i < parameterNames.length; i++) {
            paramMap.put(parameterNames[i], args[i]);
        }
        return paramMap;
    }
}
